﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio;
using Widgetsphere.Generator.Common.Util;
using Microsoft.VisualStudio.Shell;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.TextManager.Interop;
using System.Windows.Forms;
using System.ComponentModel.Design;
using tom;

namespace Widgetsphere.VsCodeTools
{
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
	[ComSourceInterfaces(typeof(IVsTextViewEvents))]
	[ComVisible(true)]
	internal sealed class EditorPane : Microsoft.VisualStudio.Shell.WindowPane,
		 IVsPersistDocData,
		 IPersistFileFormat,
		 IOleCommandTarget,
		 IVsWindowPane,
		 IVsDocDataFileChangeControl,
		 IVsFileChangeEvents
	{
		private ModelViewer newEditor = null;
		private ServiceProvider _vsServiceProvider = null;

		public EditorPane(VsCodeToolsPackage package)
			: base(null)
		{
			Control.CheckForIllegalCrossThreadCalls = false;

			//PrivateInit(package);
			newEditor = new ModelViewer(package);
		}

		public override System.Windows.Forms.IWin32Window Window
		{
			get { return newEditor; }
		}

		#region IVsPersistDocData Members

		public int Close()
		{
			try
			{
				this.Dispose();
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return VSConstants.S_OK;
		}

		public int GetGuidEditorType(out Guid pClassID)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).GetClassID(out pClassID);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pClassID = new Guid();
			}
			return retVal;
		}

		public int IsDocDataDirty(out int pfDirty)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).IsDirty(out pfDirty);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pfDirty = int.MinValue;
			}
			return retVal;
		}

		public int IsDocDataReloadable(out int pfReloadable)
		{
			try
			{
				pfReloadable = 1;
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pfReloadable = 1;
			}
			return VSConstants.S_OK;
		}

		public int LoadDocData(string pszMkDocument)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).Load(pszMkDocument, 0, 0);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return retVal;
		}

		public int OnRegisterDocData(uint docCookie, IVsHierarchy pHierNew, uint itemidNew)
		{
			return VSConstants.S_OK;
		}

		public int ReloadDocData(uint grfFlags)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).Load(null, grfFlags, 0);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return retVal;
		}

		public int RenameDocData(uint grfAttribs, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
		{
			return VSConstants.S_OK;
		}

		//private bool SaveDoc()
		//{
		//  IVsQueryEditQuerySave2 queryEditQuerySave = (IVsQueryEditQuerySave2)GetVsService(typeof(SVsQueryEditQuerySave));

		//  // Call QueryEditQuerySave
		//  uint result = 0;
		//  int hr = queryEditQuerySave.QuerySaveFile(_editor.FileName, 0, null, out result);
		//  if (ErrorHandler.Failed(hr))
		//    return false;

		//  // Call the shell to do the save for us
		//  IVsUIShell uiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
		//  string pbstrMkDocumentNew = null;
		//  int pfSaveCanceled = 0;
		//  hr = uiShell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SilentSave, (IPersistFileFormat)this, _editor.FileName, out pbstrMkDocumentNew, out pfSaveCanceled);
		//  if (ErrorHandler.Failed(hr))
		//    return false;

		//  return true;

		//}

		public int SaveDocData(VSSAVEFLAGS dwSave, out string pbstrMkDocumentNew, out int pfSaveCanceled)
		{
			return newEditor.SaveDocData(dwSave, out pbstrMkDocumentNew, out pfSaveCanceled);
		}

		public int SetUntitledDocPath(string pszDocDataPath)
		{
			return newEditor.SetUntitledDocPath(pszDocDataPath);
		}

		#endregion

		#region IPersistFileFormat Members

		private int _getClassIDRecurse = 0;
		public int GetClassID(out Guid pClassID)
		{
			_getClassIDRecurse++;
			try
			{
				if (_getClassIDRecurse < 10)
				{
					((Microsoft.VisualStudio.OLE.Interop.IPersist)this).GetClassID(out pClassID);
				}
				else
				{
					pClassID = new Guid();
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pClassID = new Guid();
			}
			finally
			{
				_getClassIDRecurse--;
			}
			return VSConstants.S_OK;
		}

		public int GetCurFile(out string ppszFilename, out uint pnFormatIndex)
		{
			return newEditor.GetCurFile(out ppszFilename, out pnFormatIndex);
		}

		public int GetFormatList(out string ppszFormatList)
		{
			try
			{
				ppszFormatList = "Model Editor files (*.wsgen) | *.wsgen";
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				ppszFormatList = string.Empty;
			}
			return VSConstants.S_OK;
		}

		public int InitNew(uint nFormatIndex)
		{
			return newEditor.InitNew(nFormatIndex);
		}

		public int IsDirty(out int pfIsDirty)
		{
			return newEditor.IsDirty(out pfIsDirty);
		}

		public int Load(string pszFilename, uint grfMode, int fReadOnly)
		{
			return newEditor.Load(pszFilename, grfMode, fReadOnly);
		}

		public int Save(string pszFilename, int fRemember, uint nFormatIndex)
		{
			return newEditor.Save(pszFilename, fRemember, nFormatIndex);
		}

		public int SaveCompleted(string pszFilename)
		{
			return VSConstants.S_OK;
		}

		#endregion

		#region IOleCommandTarget Members
		public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
		{
			return newEditor.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
		}

		public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
		{
			return newEditor.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
		}

		#endregion

		#region IVsWindowPane Members

		public int ClosePane()
		{
			try
			{
				this.Dispose(true);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return VSConstants.S_OK;
		}

		//public int CreatePaneWindow(IntPtr hwndParent, int x, int y, int cx, int cy, out IntPtr hwnd)
		//{
		//  try
		//  {
		//    Win32Methods.SetParent(Handle, hwndParent);
		//    hwnd = Handle;

		//    Size = new System.Drawing.Size(cx - x, cy - y);
		//  }
		//  catch (Exception ex)
		//  {
		//    GlobalHelper.SetErr(ex);
		//    hwnd = new IntPtr();
		//  }
		//  return VSConstants.S_OK;
		//}

		//public int GetDefaultSize(SIZE[] pSize)
		//{
		//  try
		//  {
		//    if (pSize.Length >= 1)
		//    {
		//      pSize[0].cx = Size.Width;
		//      pSize[0].cy = Size.Height;
		//    }
		//  }
		//  catch (Exception ex)
		//  {
		//    GlobalHelper.SetErr(ex);
		//  }

		//  return VSConstants.S_OK;
		//}

		public int LoadViewState(IStream pStream)
		{
			return VSConstants.S_OK;
		}

		public int SaveViewState(IStream pStream)
		{
			return VSConstants.S_OK;
		}

		public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp)
		{
			try
			{
				_vsServiceProvider = new ServiceProvider(psp);
				newEditor.SetSite(psp);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return VSConstants.S_OK;
		}

		public int TranslateAccelerator(MSG[] lpmsg)
		{
			return VSConstants.S_FALSE;
		}

		#endregion

		#region IVsDocDataFileChangeControl Members

		public int IgnoreFileChanges(int fIgnore)
		{
			return newEditor.IgnoreFileChanges(fIgnore);
		}

		#endregion

		#region IVsFileChangeEvents Members

		public int DirectoryChanged(string pszDirectory)
		{
			return VSConstants.S_OK;
		}

		public int FilesChanged(uint cChanges, string[] rgpszFile, uint[] rggrfChange)
		{
			return newEditor.FilesChanged(cChanges, rgpszFile, rggrfChange);
		}

		#endregion

		#region IVsTextViewEvents Members

		/*This interface is used as a notifier for the events that are occurring on the Text View.
				For our sample this is just provided so that the find in files scenario will work 
				properly.  It isn't necesary to implement any of the methods. */

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "pView")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "iOldLine")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "iNewLine")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "i")]
		public void OnChangeCaretLine(IVsTextView pView, int iNewLine, int iOldLine)
		{
			//Not Implemented
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "pView")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "iVisibleUnits")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "iMinUnit")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "iMaxUnits")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "iFirstVisibleUnit")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "iBar")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "i")]
		public void OnChangeScrollInfo(IVsTextView pView,
								int iBar,
								int iMinUnit,
								int iMaxUnits,
								int iVisibleUnits,
								int iFirstVisibleUnit
				)
		{
			//Not Implemented
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "pView")]
		public void OnKillFocus(IVsTextView pView)
		{
			//Not Implemented
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "pView")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "pBuffer")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")]
		public void OnSetBuffer(IVsTextView pView, IVsTextLines pBuffer)
		{
			//Not Implemented
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "pView")]
		public void OnSetFocus(IVsTextView pView)
		{
			//Not Implemented
		}
		#endregion

		#region Commands

		private void setupCommands()
		{
			// Now get the IMenuCommandService; this object is the one
			// responsible for handling the collection of commands implemented by the package.

			IMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as IMenuCommandService;
			if (null != mcs)
			{
				// Now create one object derived from MenuCommnad for each command defined in
				// the CTC file and add it to the command service.

				// For each command we have to define its id that is a unique Guid/integer pair, then
				// create the OleMenuCommand object for this command. The EventHandler object is the
				// function that will be called when the user will select the command. Then we add the 
				// OleMenuCommand to the menu service.  The addCommand helper function does all this for us.

				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.SelectAll,
				//                new EventHandler(onSelectAll), null);
				addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Copy,
												new EventHandler(onCopy), new EventHandler(onQueryCopy));
				addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Cut,
												new EventHandler(onCut), new EventHandler(onQueryCutOrDelete));
				addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Paste,
												new EventHandler(onPaste), new EventHandler(onQueryPaste));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Delete,
				//                new EventHandler(onDelete), new EventHandler(onQueryCutOrDelete));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Undo,
				//                new EventHandler(onUndo), new EventHandler(onQueryUndo));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Redo,
				//                new EventHandler(onRedo), new EventHandler(onQueryRedo));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Bold,
				//                new EventHandler(onBold), new EventHandler(onQueryBold));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Italic,
				//                new EventHandler(onItalic), new EventHandler(onQueryItalic));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Underline,
				//                new EventHandler(onUnderline), new EventHandler(onQueryUnderline));
				//addCommand(mcs, GuidList.guidVSPackage1CmdSet, (int)PkgCmdIDList.icmdStrike,
				//                new EventHandler(onStrikethrough), new EventHandler(onQueryStrikethrough));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.JustifyCenter,
				//                new EventHandler(onJustifyCenter), new EventHandler(onQueryJustifyCenter));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.JustifyLeft,
				//                new EventHandler(onJustifyLeft), new EventHandler(onQueryJustifyLeft));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.JustifyRight,
				//                new EventHandler(onJustifyRight), new EventHandler(onQueryJustifyRight));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.FontNameGetList,
				//                new EventHandler(onFontNameGetList), null);
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.FontName,
				//                new EventHandler(onFontName), null);
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.FontSizeGetList,
				//                new EventHandler(onFontSizeGetList), null);
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.FontSize,
				//                new EventHandler(onFontSize), null);
				//addCommand(mcs, VSConstants.VSStd2K, (int)VSConstants.VSStd2KCmdID.BULLETEDLIST,
				//                new EventHandler(onBulletedList), new EventHandler(onQueryBulletedList));
				// Support clipboard rings
				addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.PasteNextTBXCBItem,
												new EventHandler(onPasteNextTBXCBItem), new EventHandler(onQueryPasteNextTBXCBItem));

				//// These two commands enable Visual Studio's default undo/redo toolbar buttons.  When these
				//// buttons are clicked it triggers a multi-level undo/redo (even when we are undoing/redoing
				//// only one action.  Note that we are not implementing the multi-level undo/redo functionality,
				//// we are just adding a handler for this command so these toolbar buttons are enabled (Note that
				//// we are just reusing the undo/redo command handlers).  To implement multi-level functionality
				//// we would need to properly handle these two commands as well as MultiLevelUndoList and
				//// MultiLevelRedoList.
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.MultiLevelUndo,
				//                new EventHandler(onUndo), new EventHandler(onQueryUndo));
				//addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.MultiLevelRedo,
				//                new EventHandler(onRedo), new EventHandler(onQueryRedo));
			}
		}

		private static void addCommand(IMenuCommandService mcs, Guid menuGroup, int cmdID,
																	 EventHandler commandEvent, EventHandler queryEvent)
		{
			// Create the OleMenuCommand from the menu group, command ID, and command event
			CommandID menuCommandID = new CommandID(menuGroup, cmdID);
			OleMenuCommand command = new OleMenuCommand(commandEvent, menuCommandID);

			// Add an event handler to BeforeQueryStatus if one was passed in
			if (null != queryEvent)
			{
				command.BeforeQueryStatus += queryEvent;
			}

			// Add the command using our IMenuCommandService instance
			mcs.AddCommand(command);
		}

		private void onQueryCopy(object sender, EventArgs e)
		{
			OleMenuCommand command = (OleMenuCommand)sender;
			command.Enabled = true;
			//command.Enabled = editorControl.RichTextBoxControl.SelectionLength > 0 ? true : false;
		}

		private void onCopy(object sender, EventArgs e)
		{
			newEditor.Copy();
		}

		private void onQueryCutOrDelete(object sender, EventArgs e)
		{
			OleMenuCommand command = (OleMenuCommand)sender;
			command.Enabled = true;
			//command.Enabled = editorControl.RichTextBoxControl.SelectionLength > 0 ? true : false;
		}

		private void onCut(object sender, EventArgs e)
		{
			newEditor.Cut();
		}

		private void onQueryPaste(object sender, EventArgs e)
		{
			OleMenuCommand command = (OleMenuCommand)sender;
			//command.Enabled = editorControl.RichTextBoxControl.CanPaste(DataFormats.GetFormat(DataFormats.Text));
			command.Enabled = true;
		}

		private void onPaste(object sender, EventArgs e)
		{
			newEditor.Paste();
		}

		private void onPasteNextTBXCBItem(object sender, EventArgs e)
		{
			//// Get the Toolbox Service from the package
			//IVsToolboxClipboardCycler clipboardCycler = GetService(typeof(SVsToolbox)) as IVsToolboxClipboardCycler;

			//Microsoft.VisualStudio.OLE.Interop.IDataObject pDO;

			//ErrorHandler.ThrowOnFailure(clipboardCycler.GetAndSelectNextDataObject((IVsToolboxUser)this, out pDO));

			//ITextSelection textSelection = editorControl.TextDocument.Selection;

			//// Get the current position of the start of the current selection. 
			//// After the paste the positiono of the start of current selection
			//// will be moved to the end of inserted text, so it needs to
			//// move back to original position so that inserted text can be highlighted to 
			//// allow cycling through our clipboard items.
			//int originalStart;
			//originalStart = textSelection.Start;

			//// This will do the actual pasting of the object
			//ItemPicked(pDO);

			//// Now move the start position backwards to the original position.
			//int currentStart;
			//currentStart = textSelection.Start;
			//textSelection.MoveStart((int)tom.tomConstants.tomCharacter, originalStart - currentStart);

			//// Select the pasted text
			//textSelection.Select();
		}

		private void onQueryPasteNextTBXCBItem(object sender, EventArgs e)
		{
			//// Get the Toolbox Service from the package
			//IVsToolboxClipboardCycler clipboardCycler = GetService(typeof(SVsToolbox)) as IVsToolboxClipboardCycler;

			//int itemsAvailable;
			//ErrorHandler.ThrowOnFailure(clipboardCycler.AreDataObjectsAvailable((IVsToolboxUser)this, out itemsAvailable));

			//OleMenuCommand command = (OleMenuCommand)sender;
			//command.Enabled = ((itemsAvailable > 0) ? true : false);

			OleMenuCommand command = (OleMenuCommand)sender;
			command.Enabled = true;
		}

		#endregion

	}
}